A one-line callout that lets a person leave an instruction inside their own capture — before an agent files it — detected by a script, honored by judgement, never guessed at. This page is self-contained: open it straight from disk in any browser, no internet connection or account needed.
Every capture pipeline eventually hits the same problem: the person capturing the note knows exactly how it should be filed, and the agent processing it later has to guess from prose.
The Larry Directive closes that gap with a callout the user types themselves, right inside the capture, addressed to the orchestrator by name regardless of which agent actually executes it:
The callout syntax is the safety mechanism. Real prose — typed by the user, or pasted in from a meeting-note tool — essentially never produces > [!larry] verb by coincidence, even though that same prose is full of instruction-shaped language ("let's combine these," "file this under..."). Only an explicit callout the user typed counts. Nothing inferred from body text ever does.
One header line, an optional argument after a colon, and the prose underneath.
> [!larry] enrich > Combine my notes with the source notes & transcript. > Keep my structure; pull the hard numbers out of the transcript.
| Verb | Meaning |
|---|---|
enrich | Merge the user's notes + an imported summary + transcript into one cohesive record. |
hold | Do not file yet — an explicit defer that outlasts any in-progress heuristic (recency, a skeletal body) until the user removes it themselves. |
split | This capture covers more than one item — file one note per item. |
merge-with: [[note]] | Fold into an existing note by wikilink, rather than creating a new one. |
file-as: <folder> | Override normal routing — e.g. for sensitive material that needs a different home. |
ask | The user flagged a question or ambiguity — surface it, never guess. |
An unrecognized verb is treated as ask and surfaced — never silently dropped. A capture with more than one live directive is never guessed at either; it's reported for the user to resolve by hand.
The split that makes this safe to automate: script detection is deterministic and never re-decided; judgement honoring reads and reconciles real content.
find-directive.py <capture> scans for a [!larry] callout and reports its verb, argument, and prose. Prints NONE if there isn't one, or REVIEW — and refuses to pick — if it finds more than one.enrich, split, and merge-with require actually reading and reconciling content. file-as and ask are closer to mechanical but still route through a read of the capture, never a blind rename.find-directive.py <capture> --consume <line> --result honored|deferred|unsatisfiable rewrites the callout to [!larry-done] with today's date and the result, so it never fires again.The four things that keep this mechanism trustworthy enough to run unattended.
- [ ]) — that would collide with whatever creates tasks from checked boxes elsewhere in the vault.[!larry-done], never deleted, so the capture's own history stays legible after archiving.[!larry] callout is never guessed at — the detection script refuses, and the ambiguity is surfaced to the user.Five files. Drop them in, wire three touchpoints (below), done.
find-directive.py as part of an actual processing routine needs Python 3, but nothing to install — it's stdlib-only.Uses only Obsidian's own built-in variables (--text-normal, --text-faint, --background-modifier-border) plus one hardcoded marker-orange accent, so it renders correctly in any theme — light or dark — with no dependency on a custom theme's design tokens.
/* Larry directive callouts — portable version.
Built only on Obsidian's own CSS variables, so it renders
correctly in any theme, light or dark, with no dependency
on custom design tokens. One hardcoded accent (marker
orange) marks a directive as live; it fades to the theme's
own faint-text color once filed. */
.callout[data-callout="larry"] {
--callout-color: 217, 103, 14;
border: 1.5px dashed rgb(217, 103, 14);
border-radius: 8px;
background-color: rgba(217, 103, 14, 0.08);
box-shadow: 0 2px 10px rgba(217, 103, 14, 0.12);
transform: rotate(-1deg);
padding: 0.5em 1.1em;
margin: 1.4em 0.6em;
}
.callout[data-callout="larry"] .callout-title {
color: rgb(217, 103, 14);
font-weight: 600;
}
.callout[data-callout="larry"] .callout-title::before {
content: "📣 ";
margin-right: 0.15em;
}
.callout[data-callout="larry-done"] {
border: 1.5px solid var(--background-modifier-border);
border-radius: 8px;
background-color: transparent;
transform: none;
opacity: 0.82;
padding: 0.5em 1.1em;
margin: 1.4em 0.6em;
}
.callout[data-callout="larry-done"] .callout-title {
color: var(--text-faint);
font-weight: 600;
}
.callout[data-callout="larry-done"] .callout-title::before {
content: "🧾 ";
margin-right: 0.15em;
}
--ink-marker-*, --ink-font-hand, etc.) for a closer match to the ICOR for Life original, and won't render correctly without that theme installed.The version actually enabled in the source vault. Breaks the theme's usual flat-callout rule on purpose: a directive is an active instruction, so it gets to look like a sticky note stapled to the page — tilted, dashed, glowing while live; flattened and faded once filed.
.markdown-rendered .callout[data-callout="larry"],
.cm-s-obsidian .callout[data-callout="larry"] {
--ink-callout-ink: var(--ink-marker-text);
border-inline-start: none;
border: 1.5px dashed var(--ink-marker-border);
border-radius: var(--ink-radius-subtle);
background-color: var(--ink-marker-bg);
box-shadow: var(--ink-float-shadow), var(--ink-marker-glow);
transform: rotate(-1.5deg);
padding: 0.5em 1.1em;
margin: 1.4em 0.6em;
}
.markdown-rendered .callout[data-callout="larry"] .callout-title {
font-family: var(--ink-font-hand);
font-weight: var(--ink-hand-weight);
font-size: var(--ink-text-hand-sm);
letter-spacing: normal;
text-transform: none;
}
.callout[data-callout="larry"] .callout-title::before {
content: "📣 ";
margin-right: 0.15em;
}
.markdown-rendered .callout[data-callout="larry"] .callout-content {
font-family: var(--ink-font-hand);
font-weight: var(--ink-hand-weight-note);
font-size: var(--ink-text-hand);
line-height: 1.35;
}
.markdown-rendered .callout[data-callout="larry-done"],
.cm-s-obsidian .callout[data-callout="larry-done"] {
--ink-callout-ink: var(--ink-paper-faint);
border-inline-start: none;
border: 1.5px solid var(--ink-hairline-emphasis);
border-radius: var(--ink-radius-subtle);
background-color: transparent;
transform: none;
opacity: 0.8;
padding: 0.5em 1.1em;
margin: 1.4em 0.6em;
}
.markdown-rendered .callout[data-callout="larry-done"] .callout-title {
font-family: var(--ink-font-hand);
font-weight: var(--ink-hand-weight-note);
font-size: var(--ink-text-hand-sm);
letter-spacing: normal;
text-transform: none;
}
.callout[data-callout="larry-done"] .callout-title::before {
content: "🧾 ";
margin-right: 0.15em;
}
Keeps every other INKLINE callout's flat single-thread rendering exactly as-is, and adds one small filled corner tag — a rubber date-stamp reading DIRECTIVE / FILED — so a directive still visually belongs to the callout family at a glance.
.markdown-rendered .callout[data-callout="larry"],
.cm-s-obsidian .callout[data-callout="larry"] {
--ink-callout-ink: var(--ink-marker-text);
position: relative;
padding-top: 0.3em;
}
.markdown-rendered .callout[data-callout="larry"]::after {
content: "DIRECTIVE";
position: absolute;
top: -0.6em;
right: 0;
font-family: var(--ink-font-mono);
font-weight: 600;
font-size: 10px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--ink-on-marker);
background: var(--ink-marker-fill);
padding: 0.25em 0.6em;
border-radius: var(--ink-radius-control);
}
.callout[data-callout="larry"] .callout-title::before {
content: "📣 ";
margin-right: 0.2em;
}
.markdown-rendered .callout[data-callout="larry-done"],
.cm-s-obsidian .callout[data-callout="larry-done"] {
--ink-callout-ink: var(--ink-paper-faint);
position: relative;
padding-top: 0.3em;
opacity: 0.85;
}
.markdown-rendered .callout[data-callout="larry-done"]::after {
content: "FILED";
position: absolute;
top: -0.6em;
right: 0;
font-family: var(--ink-font-mono);
font-weight: 600;
font-size: 10px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--ink-paper-faint);
background: transparent;
border: 1px solid var(--ink-hairline-emphasis);
padding: 0.2em 0.55em;
border-radius: var(--ink-radius-control);
}
.callout[data-callout="larry-done"] .callout-title::before {
content: "🧾 ";
margin-right: 0.2em;
}
The one piece of real code in the kit — deterministic parsing, so no agent ever re-decides what a directive says. Python 3, no dependencies.
#!/usr/bin/env python3
"""Find and consume a `> [!larry] <verb>` directive callout inside a
capture. Detecting the callout, its verb, its argument, and its prose
is deterministic - this script IS the source of truth for that, never
a judgement call re-decided by whichever model reads the file.
Usage:
find-directive.py <note.md>
Scan-only. Prints each `[!larry]` callout found: its line number,
verb, argument (text after a `:` on the header line, if any), and
prose (the callout's continuation lines). Prints NONE if there is
no directive. Prints REVIEW and refuses to pick one if more than
one directive callout is found in the same file - never guesses
which one is live.
find-directive.py <note.md> --consume <line> --result honored|deferred|unsatisfiable [--note "..."]
Rewrites the directive callout starting at <line> (as reported by
a prior scan) from `[!larry]` to `[!larry-done]`, stamped with
today's date and the result, so it never fires again. Refuses if
the line at that position is not actually a `[!larry]` callout
header (the file may have changed since the scan that found it).
Recognized verbs: enrich, hold, split, merge-with, file-as, ask.
An unrecognized verb is reported as such, never silently dropped -
treat it the same as `ask` (surface it, do not guess).
"""
import argparse
import datetime
import re
import sys
from pathlib import Path
KNOWN_VERBS = {"enrich", "hold", "split", "merge-with", "file-as", "ask"}
HEADER_RE = re.compile(r"^>\s*\[!larry\]\s*([a-z-]+)\s*:?\s*(.*)$")
CONT_RE = re.compile(r"^>\s?(.*)$")
def scan(lines):
"""Return a list of dicts: {line (1-based), verb, known, argument, prose}."""
found = []
i = 0
while i < len(lines):
m = HEADER_RE.match(lines[i])
if m:
verb, rest = m.group(1), m.group(2).strip()
prose = []
j = i + 1
while j < len(lines) and lines[j].startswith(">"):
cm = CONT_RE.match(lines[j])
text = cm.group(1) if cm else ""
if text.strip():
prose.append(text.strip())
j += 1
found.append({
"line": i + 1, "verb": verb, "known": verb in KNOWN_VERBS,
"argument": rest, "prose": " ".join(prose),
})
i = j
else:
i += 1
return found
def cmd_scan(note):
lines = note.read_text(encoding="utf-8").splitlines()
found = scan(lines)
if not found:
print("NONE")
return
if len(found) > 1:
where = ", ".join(str(d["line"]) for d in found)
print(f"REVIEW: {len(found)} directive callouts found (lines {where}) - resolve by hand, do not guess which is live")
return
d = found[0]
tag = "" if d["known"] else " (UNRECOGNIZED VERB - treat as ask)"
print(f'DIRECTIVE line={d["line"]} verb={d["verb"]}{tag} argument="{d["argument"]}"')
print("PROSE:")
print(d["prose"] or "(none)")
def cmd_consume(note, line_no, result, note_text):
if result not in ("honored", "deferred", "unsatisfiable"):
sys.exit(f"FAIL --result must be honored, deferred, or unsatisfiable, got {result!r}")
lines = note.read_text(encoding="utf-8").splitlines(keepends=True)
if line_no < 1 or line_no > len(lines):
sys.exit(f"FAIL line {line_no} is out of range for {note}")
target = lines[line_no - 1]
m = HEADER_RE.match(target.rstrip("\n"))
if not m:
sys.exit(f"FAIL line {line_no} is not a `[!larry]` directive header - the file "
f"may have changed since it was scanned; re-scan before consuming")
verb, rest = m.group(1), m.group(2).strip()
today = datetime.date.today().isoformat()
suffix = f" - {note_text.strip()}" if note_text and note_text.strip() else ""
colon = f": {rest}" if rest else ""
newline = "\n" if target.endswith("\n") else ""
lines[line_no - 1] = f"> [!larry-done] {verb}{colon} ✅ {today} ({result}){suffix}{newline}"
note.write_text("".join(lines), encoding="utf-8")
print(f"OK consumed line {line_no}: {verb} -> {result} ({today})")
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("note")
ap.add_argument("--consume", type=int, metavar="LINE")
ap.add_argument("--result", choices=["honored", "deferred", "unsatisfiable"])
ap.add_argument("--note", dest="note_text", default="")
a = ap.parse_args()
note = Path(a.note).resolve()
if not note.is_file():
sys.exit(f"FAIL no such note: {note}")
if a.consume is not None:
if not a.result:
sys.exit("FAIL --consume requires --result")
cmd_consume(note, a.consume, a.result, a.note_text)
else:
if a.result:
sys.exit("FAIL --result only makes sense with --consume")
cmd_scan(note)
if __name__ == "__main__":
main()
One hotkey, pick a verb from a suggester, cursor lands on the prose line. Needs the Templater plugin.
<%*
// Insert a Larry directive callout. One hotkey -> pick a verb ->
// cursor lands on the prose line.
const directiveVerbs = ["enrich", "hold", "split", "merge-with:", "file-as:", "ask"];
const chosenVerb = await tp.system.suggester(
directiveVerbs, directiveVerbs, false, "Directive for Larry:"
);
if (!chosenVerb) return;
let verbArgument = "";
let presetProse = "";
if (chosenVerb === "enrich") {
presetProse = "";
} else if (chosenVerb === "merge-with:") {
const targetNote = await tp.system.prompt(
"Fold into which note? (leave blank to pick inline)"
);
verbArgument = targetNote ? ` [[${targetNote}]]` : " [[";
} else if (chosenVerb === "file-as:") {
const destinationFolders = [
"Sensitive", "Meetings", "Journal", "Contacts/People",
"Contacts/Companies", "Projects", "Topics", "Assets/Images", "Assets/Documents",
];
const chosenDestination = await tp.system.suggester(
destinationFolders, destinationFolders, false, "File it as:"
);
if (!chosenDestination) return;
verbArgument = ` ${chosenDestination}`;
}
-%>
> [!larry] <% chosenVerb %><% verbArgument %>
> <% presetProse ? presetProse + " " : "" %><% tp.file.cursor() %>
The template doesn't do anything until Templater knows about it and you've given it a key combo.
larry-directive-templater.md into that folder (rename it to whatever you like — the filename doesn't matter, Templater just needs it inside the templates folder).+ next to it → press your chosen combo (something unused, like Cmd/Ctrl+Shift+L for "Larry"). This opens a picker across all your Templater templates each time. If you'd rather skip the picker, Templater also registers one command per template file the moment it's in the folder — search for it by name instead ("Templater: Insert <templatename>") and bind that one directly.> [!larry] enrich (or whichever verb) already typed and your cursor on the prose line.The directive only does anything once your own capture-processing routine checks for it at three points. In ICOR for Life this lives in the SOP that processes an Inbox capture — adapt the same three touchpoints into whatever runs yours.
REVIEW (more than one found) is never guessed at — surface it to the user.enrich/split/merge-with require actually reading the content.Report every directive by name in your processing summary — honored, deferred, or unsatisfiable — the same way you'd report anything else that happened during the run. A directive that gets silently folded into a generic "processed 6 items" line defeats the point of leaving one.
If your assistant is Claude Code (or any other AI coding agent) working inside your own vault, hand it this prompt with the path to this kit filled in.
I want to add the "Larry Directive" capture-directive pattern to this ICOR for Life vault. I've unzipped a share pack to <PATH TO THIS FOLDER> containing: README.md (the full spec), find-directive.py, larry-directive-templater.md, larry-directive-portable.css, and two INKLINE-specific CSS variants. Please: 1. Read README.md in that folder for the full spec (syntax, verb table, mechanism, hard rules) before changing anything. 2. Add a new guideline note under 06 AI Team/AI Team Knowledge/Guidelines/ (pick the next unused GL-### number) capturing the syntax, verb table, mechanism, and hard rules from the README, written in this vault's own guideline voice and cross-linked from wherever the guideline index lives. 3. Copy find-directive.py into 06 AI Team/AI Team Knowledge/Scripts/find-directive.py. 4. Copy larry-directive-templater.md into 06 AI Team/AI Team Knowledge/Templates/Templater/ (or wherever Templater templates already live in this vault). Update the `destinationFolders` list inside it to match this vault's actual room names, not the ICOR for Life defaults baked into the file. 5. Ask me whether I'm running the INKLINE theme. If yes, offer both INKLINE CSS variants (margin-stamp / corner-tag) and let me pick; if no, use larry-directive-portable.css instead. Either way, drop the chosen file into .obsidian/snippets/ and tell me to enable it in Settings → Appearance → CSS snippets (you can't toggle that setting for me). 6. Walk me through setting up a Templater hotkey for the new template (Settings → Templater → confirm template folder → Settings → Hotkeys → bind "Templater: Insert Template" or the per-template command Templater registers, whichever I prefer) — don't just tell me to do it, confirm each step actually landed. 7. Find whatever SOP, guideline, or routine currently governs processing my Inbox/captures, and show me exactly where each of the three touchpoints (scan before filing, honor per verb, consume before archiving) should go in it, before editing that file. 8. Do a dry run: create one throwaway test capture containing a `[!larry] enrich` callout, walk it through the updated processing routine, confirm it gets consumed to `[!larry-done]` with today's date, then delete the test file and report back. Ask me before overwriting anything that already exists at any of these paths, and stop and ask if step 2's guideline numbering or step 7's target file is ambiguous rather than guessing.